package com.raccoonagents.tools.cascet2d.model.particle;

import java.util.ArrayList;
import java.util.List;

import com.raccoonagents.tools.cascet2d.interfaces.ISynchronizedUpdateable;
import com.raccoonagents.tools.cascet2d.interfaces.rendering.IRenderable;
import com.raccoonagents.tools.cascet2d.model.Vector2f;
import com.raccoonagents.tools.cascet2d.util.CalculationUtils;


public class ParticleSystem implements ISynchronizedUpdateable, IRenderable{
	
	protected int directionAngle;
	protected int spreadAngle;
	protected int burstAmount;
	protected boolean activated;
	protected long timeIntervalElapsed;
	
	protected List<Particle> lastPassGeneratedParticles = new ArrayList<Particle>(200);	
	protected List<Particle> allGeneratedParticles = new ArrayList<Particle>(200);
	protected List<Particle> removeList = new ArrayList<Particle>(200);
	
	protected Particle particleType;
	protected final Vector2f location;
	protected boolean autoMaintain = true;
	protected float positionOffset;
	protected int autoMaintainParticlesPerUpdate = 100;
	protected int particleMinSize = 10;
	protected int particleSizeVariance = 0;
	protected float particleMinSpeed = 0.0f;
	public float getParticleMinSpeed() {
		return particleMinSpeed;
	}

	public void setParticleMinSpeed(final float particleMinSpeed) {
		this.particleMinSpeed = particleMinSpeed;
	}

	protected float particleMaxSpeed = 0.1f;
	
	public float getParticleMaxSpeed() {
		return particleMaxSpeed;
	}

	public void setParticleMaxSpeed(final float particleMaxSpeed) {
		this.particleMaxSpeed = particleMaxSpeed;
	}

	protected float getNewSpeed(){
		return (float) ((Math.random() * (float)particleMaxSpeed) + particleMinSpeed);
	}
	
	protected int maxEmissions = -1;
	public int getMaxEmissions() {
		return maxEmissions;
	}

	public void setMaxEmissions(final int maxEmissions) {
		this.maxEmissions = maxEmissions;
	}

	protected int currentEmissions = 0;

	public int getParticleSizeVariance() {
		return particleSizeVariance;
	}

	public void setParticleSizeVariance(final int particleSizeVariance) {
		this.particleSizeVariance = particleSizeVariance;
	}

	public int getParticleMinSize() {
		return particleMinSize;
	}

	public void setParticleMinSize(final int particleMinSize) {
		this.particleMinSize = particleMinSize;
	}


	public int getAutoMaintainParticlesPerUpdate() {
		return autoMaintainParticlesPerUpdate;
	}

	public void setAutoMaintainParticlesPerUpdate(final int autoMaintainParticlesPerUpdate) {
		this.autoMaintainParticlesPerUpdate = autoMaintainParticlesPerUpdate;
	}

	public int getDirectionAngle() {
		return directionAngle;
	}

	void removeParticle(final Particle p){
		removeList.add(p);
	}
	
	public void setDirectionAngle(final int directionAngle) {
		this.directionAngle = directionAngle;
	}

	public int getSpreadAngle() {
		return spreadAngle;
	}

	public void setSpreadAngle(final int spreadAngle) {
		this.spreadAngle = spreadAngle;
	}

	public int getBurstAmount() {
		return burstAmount;
	}

	public void setBurstAmount(final int burstAmount) {
		this.burstAmount = burstAmount;
	}

	public Particle getParticleType() {
		return particleType;
	}

	public void setParticleType(final Particle particleType) {
		this.particleType = particleType;
	}	
	
	public Vector2f getLocation() {
		return location;
	}

	protected long timeInterval;
	



	/**
	 * 
	 * @return returns generated particles from last iteration.
	 * particles here exist for only one round in a update, and 
	 * they will be cleared on next update. 
	 * 
	 */
	public List<Particle> getLastPassGeneratedParticles() {
		return lastPassGeneratedParticles;
	}

	public boolean isActivated() {
		return activated;
	}

	public void setActivated(final boolean activated) {
		this.activated = activated;
	}

	/**
	 * 
	 * @param location
	 * @param directionAngle
	 * @param angle
	 * @param timeInterval
	 * @param burstAmount
	 * @param positionOffset
	 * @param modelParticle
	 */
	public ParticleSystem(final Vector2f location, final int directionAngle,  final int angle, final int timeInterval, final int burstAmount, final float positionOffset, final Particle modelParticle){
		this.spreadAngle = angle;
		this.directionAngle = directionAngle;
		this.timeInterval = timeInterval;
		this.burstAmount = burstAmount;
		this.timeIntervalElapsed = timeInterval;
		this.particleType = modelParticle;
		this.location = location;
		this.positionOffset = positionOffset;
	}

	@Override
	public void update(final long delta) {
		if(activated){
			timeIntervalElapsed -= delta;
			if(timeIntervalElapsed <=0){
				if(maxEmissions == -1){
					emit();					
				}else{
					if(currentEmissions < maxEmissions){
						emit();
						currentEmissions ++;
					}else{
						activated = false;
					}
				}
				// reset back time
				timeIntervalElapsed = timeInterval;
			}
		}
		if(autoMaintain){
			
			//final int deleteAmount = (int) (((float)burstAmount / (float)timeInterval) * delta);
			//final int deleteAmount = (int) (removePerMs * delta);
				
			final int removePCount = removeList.size() > autoMaintainParticlesPerUpdate ? autoMaintainParticlesPerUpdate : removeList.size();
		
			for(int i = 0; i < removePCount; i++){
				allGeneratedParticles.remove(removeList.remove(0));
			}
				
				
			// add generated particles incrementally to smoothen framerate.
			final int pCount = lastPassGeneratedParticles.size() > autoMaintainParticlesPerUpdate ? autoMaintainParticlesPerUpdate : lastPassGeneratedParticles.size();
			
			for(int i = 0; i < pCount; i++){
				allGeneratedParticles.add(lastPassGeneratedParticles.remove(0));
			}
				
			
			for(final Particle p : allGeneratedParticles){
				p.update(delta);
			}
		}
			
			//System.out.println("RL = " + removeList.size());
			//System.out.println("GEN = " + allGeneratedParticles.size());
						
	}
	/*
	public boolean hasAliveParticles(){
		return allGeneratedParticles.size() >  0;
	}*/
	/**
	 * this is true, if all particles to be maintained by this system are gone
	 * this system has been deactivated.
	 */
	public boolean isFinished(){
		return allGeneratedParticles.size() <= 0 && !isActivated();
	}
	
	protected void emit(){
		for(int i = 0; i < burstAmount; i++){
			final Particle p = particleType.copy(this);
			p.setSystem(this);
			// set position
			
			final Vector2f unitDirV = CalculationUtils.getZUnitVector((float) (directionAngle + (Math.random() * spreadAngle)) - spreadAngle / 2);
			final float pxOffset = (float) (Math.random() * positionOffset / 2) - positionOffset / 2;
			final float pyOffset = (float) (Math.random() * positionOffset / 2) - positionOffset / 2;

			p.setLocation(new Vector2f(this.location.x + pxOffset, this.location.y + pyOffset));
			p.direction = unitDirV;			
			lastPassGeneratedParticles.add(p);
		}
	}

	public boolean isAutoMaintain() {
		return autoMaintain;
	}

	public void setAutoMaintain(final boolean autoMaintain) {
		this.autoMaintain = autoMaintain;
	}

	@Override
	public void render() {
		for(final Particle p : allGeneratedParticles){
			p.render();
		}
		
	}
	
}
